using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FlatRedBall;
using FlatRedBall.Graphics;
using FlatRedBall.Math.Geometry;
using FlatRedBall.Input;

using Microsoft.Xna.Framework;

using Xzipit.Utilities;
using Xzipit.Utilities.Gestures;

namespace Xzipit.GameObjects
{
    class Necklace : Obstacle
    {
        #region Fragment Class
        private class NecklaceFragment : PositionedObject
        {
            private const float BASE_VELOCITY = 15.0f;
            private const float BOUNDARY = 30.0f;
            private Sprite _sprite;
            private Circle _collision;

            #region Properties
            public Circle Collision
            {
                get { return _collision; }
                set { _collision = value; }
            }

            public float Alpha
            {
                get { return _sprite.Alpha; }
                set { _sprite.Alpha = value; }
            }

            public float AlphaRate
            {
                get { return _sprite.AlphaRate; }
                set { _sprite.AlphaRate = value; }

            }

            public bool Visible
            {
                get { return _sprite.Visible; }
            }

            #endregion

            public NecklaceFragment(float dir)
            {
                this.RelativeRotationZ = dir;
                Initialize();
            }

            private void Initialize()
            {
                SpriteManager.AddPositionedObject(this);

                _sprite = SpriteManager.AddSprite(@"Content\GameObjects\Obstacles\Necklace\break", "Global");
                _sprite.AttachTo(this, false);
                _sprite.PixelScale();
                _sprite.Visible = false;

                _collision = ShapeManager.AddCircle();
                _collision.Radius = _sprite.ScaleX;
                _collision.AttachTo(this, false);
                _collision.Visible = false;
                
            }

            // launches the fragment in it's respective direction.
            public void Launch()
            {
                if (!_sprite.Visible)
                {
                    _sprite.Visible = true;
                    this.RelativeVelocity.X = BASE_VELOCITY * (float)Math.Cos(this.RelativeRotationZ);
                    this.RelativeVelocity.Y = BASE_VELOCITY * (float)Math.Sin(this.RelativeRotationZ);
                }
            }

            // caps the distance that the fragments can move in the x and y directions
            public void TryStop()
            {
                if (_sprite.Visible)
                {
                    if (this.RelativeX < -BOUNDARY || this.RelativeX > BOUNDARY
                        || this.RelativeY < -BOUNDARY || this.RelativeY > BOUNDARY)
                    {
                        this.RelativeVelocity = Vector3.Zero;
                        _sprite.Visible = false;
                    }
                }
            }

            public void Destroy()
            {
                SpriteManager.RemoveSprite(_sprite);
                SpriteManager.RemovePositionedObject(this);
                ShapeManager.Remove(_collision);
            }
        }

        #endregion

        #region Fields

        // Keep the ContentManager for easy access:
        string _contentManagerName;

        Vector3 _direction;
        Vector3 _startPos;
        Vector3 _endPos;

        private Circle _startCircle;

        private List<Sprite> _chain;
        private List<NecklaceFragment> _fragments;
        private const float TEXTURE_HEIGHT = .48132037f;
        private const int TOTAL_FRAGMENTS = 9;

        private double _collideTime;

        #endregion

        #region Properties
        // Override the Alpha and AlphaRate property of Obstacle
        public override float Alpha
        {
            get { return _sprite.Alpha; }
            set
            {
                _sprite.Alpha = value;
                foreach (Sprite s in _chain)
                {
                    s.Alpha = value;
                }
                foreach (NecklaceFragment f in _fragments)
                {
                    f.Alpha = value;
                }
            }
        }

        public override float AlphaRate
        {
            get { return _sprite.AlphaRate; }
            set
            {
                _sprite.AlphaRate = value;
                foreach (Sprite s in _chain)
                {
                    s.AlphaRate = value;
                }
                foreach (NecklaceFragment f in _fragments)
                {
                    f.AlphaRate = value;
                }
            }
        }

        #endregion

        #region Methods

        #region Constructors
        public Necklace(string contentManagerName, float x, float y, Vector3 direction)
        {
            // Read in a necklace from a level file.
            _contentManagerName = contentManagerName;

            this.Position.X = x;
            this.Position.Y = y;
            this.Position.Z = 0.1f;
            _startPos = new Vector3(x, y, 0.1f);
            _endPos = direction;
            _endPos.Z = 0.1f;

            _direction = direction - this.Position;
            _direction.Normalize();
            _direction *= 2;

            // The indicator that comes on screen if it is a tutorial level.
            SetTooltip("dodge");
            _tooltip.RelativeY = 2.0f;
            _tooltip.RelativeZ = 0.15f;

            Initialize(true);
        }
        #endregion

        #region Initialization
        protected override void Initialize(bool addToManagers)
        {
            if (addToManagers)
            {
                AddToManagers();
            }
        }

        private void InitializeFragments()
        {
            float baseDirection = (2 * (float)Math.PI) / TOTAL_FRAGMENTS;
            _fragments = new List<NecklaceFragment>();
            for (int i = 0; i < TOTAL_FRAGMENTS; i++)
            {
                NecklaceFragment nf = new NecklaceFragment(i * baseDirection);
                nf.AttachTo(this, false);
                _fragments.Add(nf);
            }
        }

        private void InitializeChain()
        {
            _chain = new List<Sprite>();

            // calculate the distance between start and end nodes.
            float realLength = Math.Abs((_startPos - _endPos).Length() / TEXTURE_HEIGHT);
            int length = (int)realLength;

            // round up always so we get an integer number of segments.
            if (realLength - length > 0.0f)
            {
                length++;
            }

            // create a sprite and collision for all segments of the track.
            for (int i = 0; i < length; i++)
            {
                Sprite temp = SpriteManager.AddSprite(@"Content\GameObjects\Obstacles\Necklace\chain", _contentManagerName);
                temp.AttachTo(_startCircle, false);
                temp.PixelScale();
                temp.RelativeY = 0.0f - i * (2 * temp.ScaleY);
                temp.RelativeX = 0.0f + TEXTURE_HEIGHT * 2.0f;
                temp.RelativeZ = -.01f;
                temp.Alpha = 0.8f;

                _chain.Add(temp);
            }

            bool flip = _startPos.X - _endPos.X > 0;

            // Rotates the chain so it is positioned correctly between 2 nodes.
            float faceAtX = (flip) ? _endPos.X - _startPos.X : _startPos.X - _endPos.X;
            //float faceAtX = _startPos.X - _endPos.X;
            float faceAtY = (flip) ? _endPos.Y - _startPos.Y : _startPos.Y - _endPos.Y;
            //float faceAtY = _startPos.Y - _endPos.Y;

            float rotation = (float)Math.Atan2(faceAtY, faceAtX) - (float)Math.PI / 2.0f;

            _startCircle.RotationZ = rotation;
            _sprite.RelativeRotationZ = rotation - (float)Math.PI / 2;

            if (flip)
            {
                _startCircle.Position = _endPos;
            }
            else
            {
                _startCircle.Position = _startPos;
            }
        }

        public virtual void AddToManagers()
        {
            SpriteManager.AddPositionedObject(this);

            _sprite = SpriteManager.AddSprite(@"Content\GameObjects\Obstacles\Necklace\necklace", _contentManagerName);

            base.AddToManagers(0.35f, 0.8f);

            _startCircle = ShapeManager.AddCircle();
            _startCircle.Radius = 0.0f;
            
            _startCircle.Visible = true;

            InitializeChain();
            InitializeFragments();

            // sets the starting velocity in the direction specified in the level file.
            _direction.Z = 0.0f;
            this.Velocity = _direction;
            
            _collideTime = -1.0;
        }

        #endregion

        #region Update
        public override void Activity(GameGesture zipGesture, double t)
        {
            if (_enabled)
            {
                // Reverses the direction of the necklace once it reaches the position
                // specified in the level file.
                if ((this.Position - _endPos).Length() < 0.1f)
                {
                    this.Velocity *= -1;
                    Vector3 temp = _endPos;
                    _endPos = _startPos;
                    _startPos = temp;
                    _direction = _endPos - this.Position;
                    _direction.Normalize();
                    _direction *= 2;
                    _direction.Z = 0.0f;
                    this.Velocity = _direction;
                }

                if (_tooltip.Visible)
                {
                    PlayTooltip();
                }

            }
            else
            {
                // stop the fragments after a certain distance.
                foreach (NecklaceFragment nf in _fragments)
                {
                    nf.TryStop();
                }
            }
        }

        public void Collide()
        {
            _enabled = false;
            _sprite.Visible = false;
            this.Velocity = Vector3.Zero;

            foreach (NecklaceFragment nf in _fragments)
            {
                nf.Launch();
            }
        }

        public override void Collide(double t)
        {
            if (_collideTime != -1.0 && t - _collideTime > 0.5)
            {
                Collide();
            }
            else if (_collideTime == -1.0)
            {
                _collideTime = t;
            }
        }

        public bool CollideWithNecklace(Necklace other)
        {
            if (other != this)
            {
                foreach (NecklaceFragment nf in _fragments)
                {
                    if (nf.Visible && other.Collision.CollideAgainst(nf.Collision))
                    {
                        other.Collide();
                        return true;
                    }
                }
            }

            return false;
        }

        #endregion

        #region Destroy
        public override void Destroy()
        {
            base.Destroy();

            ShapeManager.Remove(_startCircle);

            foreach (Sprite s in _chain)
            {
                SpriteManager.RemoveSprite(s);
            }

            foreach (NecklaceFragment nf in _fragments)
            {
                nf.Destroy();
            }
        }

        #endregion

        #region Private Methods

        #region Tooltip
        // Flashes the tap this indicator.
        protected override void PlayTooltip()
        {
            if (_tooltip.Alpha == 0.0f)
            {
                _tooltip.AlphaRate = 4.5f;
            }
            else if (_tooltip.Alpha == 1.0f)
            {
                _tooltip.AlphaRate = -1.0f;
            }
        }

        
        #endregion

        #endregion

        #endregion

    }
}
